﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Licenta1.RayTracer
{
    class CSG:MathShape
    {
        MathShape A, B;
        Operator oper;

        public enum Operator { Union, Intersection, Difference, };

        public CSG(MathShape s1, MathShape s2, Operator o)
        {
            A = s1;
            B = s2;
            oper = o;
        }

        public override double[] Intersects(Ray ray)
        {
            if (oper == Operator.Union)
            {
                double[] intersections_a = A.Intersects(ray);
                double[] intersections_b = B.Intersects(ray);

                List<double> final = new List<double>();

                foreach (double intersection in intersections_a)
                    if (!B.IsInside(ray.point + ray.direction * intersection))
                        final.Add(intersection);

                foreach (double intersection in intersections_b)
                    if (!A.IsInside(ray.point + ray.direction * intersection))
                        final.Add(intersection);

                return final.ToArray();
            }
            else if (oper == Operator.Intersection)
            {
                double[] intersections_a = A.Intersects(ray);
                double[] intersections_b = B.Intersects(ray);

                List<double> final = new List<double>();

                foreach (double intersection in intersections_a)
                    if (B.IsInside(ray.point + ray.direction * intersection))
                        final.Add(intersection);

                foreach (double intersection in intersections_b)
                    if (A.IsInside(ray.point + ray.direction * intersection))
                        final.Add(intersection);

                return final.ToArray();
            }
            else if (oper == Operator.Difference)
            {
                double[] intersections_a = A.Intersects(ray);
                double[] intersections_b = B.Intersects(ray);

                List<double> final = new List<double>();

                foreach (double intersection in intersections_a)
                    if (!B.IsInside(ray.point + ray.direction * intersection))
                        final.Add(intersection);

                foreach (double intersection in intersections_b)
                    if (A.IsInside(ray.point + ray.direction * intersection))
                        final.Add(intersection);

                return final.ToArray();
            }
            else
                throw new Exception("Exception 1.:D");
        }

        public override Vector GetNormal(Vector surfacePoint)
        {
            if (oper == Operator.Union ||
                oper == Operator.Intersection)
            {
                if (A.IsOnSurface(surfacePoint))
                    return A.GetNormal(surfacePoint);
                else if (B.IsOnSurface(surfacePoint))
                    return B.GetNormal(surfacePoint);
                else
                    throw new Exception("Get CSG normal failed.");
            }
            else if (oper == Operator.Difference)
            {
                if (A.IsOnSurface(surfacePoint))
                    return A.GetNormal(surfacePoint);
                else if (B.IsOnSurface(surfacePoint))
                    return B.GetNormal(surfacePoint) * -1;
                else
                    throw new Exception("Get CSG normal failed.");
            }
            else
                throw new Exception("Exception 4.:D");
        }

        public override bool IsInside(Vector point)
        {
            if (oper == Operator.Union)
            {
                return A.IsInside(point) || B.IsInside(point);
            }
            else if (oper == Operator.Intersection)
            {
                return A.IsInside(point) && B.IsInside(point);
            }
            else if (oper == Operator.Difference)
            {
                return A.IsInside(point) && !B.IsInside(point);
            }
            else
                throw new Exception("Exception 2.:D");
        }

        public override bool IsOnSurface(Vector point)
        {
            if (oper == Operator.Union)
            {
                return (A.IsOnSurface(point) && !B.IsInside(point)) ||
                    (B.IsOnSurface(point) && !A.IsInside(point));
            }
            else if (oper == Operator.Intersection)
            {
                return (A.IsOnSurface(point) && B.IsInside(point)) ||
                    (B.IsOnSurface(point) && A.IsInside(point));
            }
            else if (oper == Operator.Difference)
            {
                return (A.IsOnSurface(point) && !B.IsInside(point)) ||
                    (B.IsOnSurface(point) && A.IsInside(point));
            }
            else
                throw new Exception("Exception 3.:D");
        }
    }
}
